package com.lfz.demo.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lfz.demo.entity.Menu;
import com.lfz.demo.entity.Menu;
import com.lfz.demo.entity.User;
import com.lfz.demo.service.MenuService;
import com.lfz.demo.service.RoleService;
import com.lfz.demo.utils.RedisUtil;
import com.lfz.demo.utils.TreeNode;
import com.lfz.demo.utils.TreeNodeBuilder;
import com.lfz.demo.vo.DataView;
import com.lfz.demo.vo.MenuVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.*;

import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * @author lfz
 * @date 2025/3/3 15:30
 */
@RestController
@RequestMapping("/menu")
public class MenuController {

    private static final Logger logger = LoggerFactory.getLogger(MenuController.class);
    // 缓存时间常量（单位：分钟）
    private static final int CACHE_EXPIRE = 1440; //缓存过期时间（1440分钟）。
    private static final String CACHE_PREFIX = "menu:"; //缓存键前缀，避免键冲突。

    @Autowired
    private MenuService menuService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 加载所有菜单
     * @param menuVo
     * @return
     */
    @GetMapping("/loadAllMenu")
    public DataView loadAllMenu(MenuVo menuVo){
        // 第一种方法 mybatis-plus 分页
        // 查询所有带有模糊查询、等值查询条件 带有分页
//        IPage<Menu> page = new Page<>(menuVo.getPage(),menuVo.getLimit());
//        QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
//        // 模糊查询
//        queryWrapper.like(StringUtils.isNoneBlank(menuVo.getTitle()),"title",menuVo.getTitle());
//        queryWrapper.orderByAsc("ordernum");
//        menuService.page(page,queryWrapper);
//        return new DataView(page.getTotal(), page.getRecords());

        String cacheKey = CACHE_PREFIX + "loadAllMenu" + String.format(", page%s, limit%s",
                menuVo.getPage(),
                menuVo.getLimit()
        );

        try {
            // 尝试从redis缓存获取数据
            DataView cacheData = (DataView) redisUtil.get(cacheKey);
            if (cacheData != null){
                return cacheData;
            }
        } catch (Exception e){
            e.printStackTrace();
            logger.error("Redis连接失败，降级查询Mysql");
        }

        try {
            // 第二种方法 jpa 分页
            // 创建分页对象
            Pageable pageable = PageRequest.of(menuVo.getPage() - 1, menuVo.getLimit());

            // 创建查询条件
            Specification<Menu> spec = (root, query, criteriaBuilder) -> {
                List<Predicate> predicates = new ArrayList<>();
                if (menuVo.getTitle() != null) {
                    predicates.add(criteriaBuilder.like(root.get("title"), "%" + menuVo.getTitle() + "%"));
                }

                query.orderBy(criteriaBuilder.desc(root.get("ordernum")));
                return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
            };

            // 分页查询
            org.springframework.data.domain.Page<Menu> page = menuService.findByPage(spec, pageable);

            // 返回数据封装
            DataView dataView = new DataView(page.getTotalElements(), page.getContent());

            // 设置redis缓存
            redisUtil.set(cacheKey, dataView, CACHE_EXPIRE);
            return dataView;
        } catch (Exception e){
            e.printStackTrace();
            logger.error("菜单管理页查询异常", e);
            return new DataView(0L, Collections.emptyList());
        }
    }

    /**
     * 加载下拉菜单数据 初始化下拉树
     */
    @PostMapping("/loadMenuManagerLeftTreeJson")
    public DataView loadMenuManagerLeftTreeJson(){

        List<Menu> list = menuService.list();
        List<TreeNode> treeNodes = new ArrayList<>();
        for (Menu menu : list) {
            Boolean open = menu.getOpen() == 1 ? true : false;
            treeNodes.add(new TreeNode(menu.getId(),menu.getPid(),menu.getTitle(),open));
        }
        return new DataView(treeNodes);
    }

    /**
     * 赋值最大排序码加 1
     * 条件查询：倒叙排序，取一条数据，+1
     */
    @PostMapping("/loadMenuMaxOrderNum")
    public Map<String,Object> loadMenuMaxOrderNum(){
        Map<String,Object> map = new HashMap<>();
        QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("ordernum");
        IPage<Menu> page = new Page<>(1,1);
        List<Menu> list = menuService.page(page, queryWrapper).getRecords();
        map.put("value",list.get(0).getOrdernum()+1);
        return map;
    }

    /**
     * 新增菜单逻辑
     */
    @PostMapping("/addMenu")
    public DataView addMenu(Menu menu){
        DataView dataView = new DataView();
        menu.setType("menu");
        boolean save = menuService.save(menu);
        if (!save){
            dataView.setCode(100);
            dataView.setMsg("数据插入失败！");
        }
        // 清理相关菜单缓存
        clearRelatedCaches();
        dataView.setCode(200);
        dataView.setMsg("菜单插入成功！");
        return dataView;
    }

    /**
     * 更新菜单
     */
    @PostMapping("/updateMenu")
    public DataView updateMenu(Menu menu){
        boolean b = menuService.updateById(menu);
        DataView dataView = new DataView();
        if (b){
            // 清理相关菜单缓存
            clearRelatedCaches();
            dataView.setCode(200);
            dataView.setMsg("更新菜单成功！");
        } else {
            dataView.setCode(100);
            dataView.setMsg("更新菜单失败！");
        }
        return dataView;
    }

    /**
     * 判断有没有子类ID
     * 没有子类ID，可以删除
     */
    @PostMapping("/checkMenuHasChildrenNode")
    public Map<String,Object> checkChildrenNode(Menu menu){
        Map<String,Object> map = new HashMap<>();
        QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("pid",menu.getId());
        List<Menu> list = menuService.list(queryWrapper);
        if (list.size() > 0){
            map.put("value",true);
        } else {
            map.put("value",false);
        }
        return map;
    }

    /**
     * 通过了判断的可以真正删除
     */
    @PostMapping("/deleteMenu")
    public DataView deleteMenu(Menu menu){
        DataView dataView = new DataView();
        boolean b = menuService.removeById(menu);
        if (b){
            // 清理相关菜单缓存
            clearRelatedCaches();
            dataView.setCode(200);
            dataView.setMsg("删除菜单成功！");
        } else {
            dataView.setCode(100);
            dataView.setMsg("删除菜单失败！");
        }
        return dataView;
    }

    /**
     * 加载主页面 index 的菜单栏，带有层级关系
     */
    @GetMapping("/loadIndexLeftMenuJson")
    public DataView loadIndexLeftMenuJson(HttpSession session){
        // 从session中获取到user
        User user = (User) session.getAttribute("user");
        // 从user中获取对应的用户名及姓名
        String username = user.getUsername();
        String name = user.getName();

        String cacheKey = CACHE_PREFIX + "loadIndexLeftMenuJson, username:" + username + ", name:" + name; //生成缓存键

        try {
            // 尝试从redis缓存获取数据
            DataView cacheData = (DataView) redisUtil.get(cacheKey);
            // 判断是否有数据
            if (cacheData != null){
                return cacheData;
            }
        }catch (Exception e){
            e.printStackTrace();
            logger.error("Redis连接失败，降级查询Mysql");
        }

        try {
            // 根据条件查询菜单栏  【管理员，其他 学生、老师 “条件查询”】
            QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
            List<Menu> list = null;
            // 1、取出session中的用户ID
            Integer userid = user.getId();

            if (user.getUsername().equals("admin")){
                list = menuService.list();  // 获取全部菜单权限
            }else{
                // 2、根据用户ID查询角色
                List<Integer> currentUserRoleIds = roleService.queryUserRoleById(userid);
                logger.info("用户 {} 的角色 ID 列表: {}", userid, currentUserRoleIds);

                // 3、去重
                /**
                1) 用户可能拥有多个角色
                2) 提高查询效率
                3) 保证数据的准确性和一致性
                 */
                Set<Integer> mids = new HashSet<>();
                for (Integer rid : currentUserRoleIds) {
                    // 3.1、根据角色ID查询菜单ID
                    List<Integer> permissionIds = roleService.queryAllPermissionsByRid(rid);
                    logger.info("角色 {} 的菜单 ID 列表: {}", rid, permissionIds);

                    // 3.2、菜单栏ID和角色ID去重
                    mids.addAll(permissionIds);
                }
                // 4、根据角色ID去查询菜单栏ID
                if (mids.size() > 0) {
                    queryWrapper.in("id", mids);
                    list = menuService.list(queryWrapper);
                }
            }


            // 5、构造层级关系
            List<TreeNode> treeNodes = new ArrayList<>();
            for (Menu m : list){
                Integer id = m.getId();
                Integer pid = m.getPid();
                String title = m.getTitle();
                String icon = m.getIcon();
                String href = m.getHref();
                Boolean open = m.getOpen().equals(1)?true:false;
                treeNodes.add(new TreeNode(id,pid,title,icon,href,open));
            }
            // 层级关系
            List<TreeNode> nodeList = TreeNodeBuilder.build(treeNodes, 0);

            // 设置redis缓存
            redisUtil.set(cacheKey, new DataView(nodeList), CACHE_EXPIRE);
            return new DataView(nodeList);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("根据用户显示对应权限菜单异常");
            return new DataView(0L, Collections.emptyList());
        }

    }

    /**
     * 清理相关缓存
     */
    private void clearRelatedCaches() {
        try {
            redisUtil.deletePattern(CACHE_PREFIX + "*");
//            logger.info("成功清理菜单相关缓存");
        } catch (Exception e) {
            logger.error("缓存清理失败", e);
        }
    }

}
